/*
 ============================================================================
 Name        : SierpinskyTriangle.c
 Author      : Jorge García
 Version     : 1.0
 Copyright   : Bajo los terminos de la licencia GNU GPL V 3 y posteriores
 Description : Programa que dibuja el fractal del triangulo aleatorio de
 	 	 	   Sierpinsky, con tres puntos inciales introducidos con el raton
 ============================================================================
 */
#include <GL/glut.h>
#include <GL/gl.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>

typedef struct {
	float x, y;
}Punto;

void inicializa(void);
void dibuja(void);
void sierpinskyTriangle(Punto t1, Punto t2, Punto t3);
void raton(int boton, int estado, int mx, int my);
void teclado(unsigned char key, int mx, int my);
void tecladoEspecial(int key, int mx, int my);
Punto seleccionaAzar(Punto p0, Punto p1, Punto p2);
void randomColor(float color[]);

/* Arreglo para guardar los vertices del triangulo */
Punto vertices[3];
/* Numero de iteraciones */
int numIteraciones;
/* Numero de vertices introducidos al momento */
int numPuntos;
/* Alto de la ventana, para interactuar con el raton y el glut */
int alto_ventana;
/* Tamaño del punto en pixeles: Tamaño actual, rango y delta */
float size, range[2], delta;

int main(int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowSize(640,480);
    glutInitWindowPosition(100, 150);
    glutCreateWindow("Fractal del triangulo aleatorio de Sierpinsky");
    inicializa();

    glutDisplayFunc(dibuja);
    glutMouseFunc(raton);
    glutKeyboardFunc(teclado);
    glutSpecialFunc(tecladoEspecial);
    glutMainLoop();

    return 0;
}

void inicializa() {
    glClearColor(1.0, 1.0, 1.0, 1.0);
    /*Tamaño de puntos y el antialias a todo lo que da*/
    glPointSize(3.0);
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	/*Tamaño maximo y minimo de un punto en esta tarjeta*/
	glGetFloatv(GL_POINT_SIZE_RANGE, range); 
	/* Que tanto se puede aumentar o diminuir un punto en esta tarjeta*/
	glGetFloatv(GL_POINT_SIZE_GRANULARITY, &delta);

	/* Planta una semilla para el generador de numero aleatorios */
    srand(time(NULL));
	
	/* Valores iniciales de variables de interaccion */
    numIteraciones = 1000;
    numPuntos = 0;
    size = 3.0;
	
	/* Poner el sistema de refrencia */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 640.0, 0.0, 480.0);
    alto_ventana = 480;
}

void dibuja(void) {
	int i;

    glClear(GL_COLOR_BUFFER_BIT);

    /* Hay tres puntos pinta el fractal */
    if (numPuntos == 3) {
    	sierpinskyTriangle(vertices[0], vertices[1], vertices[2]);
    } else {
    	/* Si aun no hay tres vertices pinta los que lleves en color negro*/
    	glColor3f(0.0, 0.0, 0.0);
		glBegin(GL_POINTS);
		for (i = 0; i < numPuntos; i++) {
			glVertex2f(vertices[i].x, vertices[i].y);
		}
		glEnd();
    }

    glFlush();
}

void raton(int boton, int estado, int mx, int my) {

	if (boton == GLUT_LEFT_BUTTON && estado == GLUT_DOWN) {
		/* Si ya tenias tres puntos, el siguiente es el primero de un
		nuevo fractal */
		if (numPuntos == 3) {
			numPuntos = 0;
		}
		/* Registra el punto que te acaban de dar */
		vertices[numPuntos].x = mx;
		vertices[numPuntos].y = alto_ventana - my;
		numPuntos++;

	}

	glutPostRedisplay();
}

/* Pinta un triangulo aleatorio de Sierpinsky */
void sierpinskyTriangle(Punto t1, Punto t2, Punto t3) {
	Punto p, t;
	float color[3];
	int i;

	p = seleccionaAzar(t1, t2, t3);
	glBegin(GL_POINTS);
	for (i = 0; i < numIteraciones; i++) {
		t = seleccionaAzar(t1, t2, t3);
		p.x = (p.x + t.x) / 2.0;
		p.y = (p.y + t.y) / 2.0;
		
		/* Un color al azar para cada punto */
		randomColor(color);
		glColor3fv(color);
		
		glVertex2f(p.x, p.y);
	}
	glEnd();
}

/* Con las teclas de flechas, aumenta o disminuye el tamaño de los
 * Puntos en pixles y aumenta o disminuye el numero de iteraciones */
void tecladoEspecial(int key, int mx, int my) {
	
	switch(key) {
		case GLUT_KEY_UP:
			if (size < range[1]) {
				size += delta;
				glPointSize(size);
			}
		break;

		case GLUT_KEY_DOWN:
			if (size > range[0]) {
				size -= delta;
				glPointSize(size);
			}
		break;
		
		case GLUT_KEY_RIGHT:
			if (numIteraciones != 10000) numIteraciones += 1000;
		break;
		
		case GLUT_KEY_LEFT:
			if (numIteraciones != 1000) numIteraciones -= 1000;
		break;
	}

	glutPostRedisplay();
}

/* Con la tecla Esc termina el programa sin errores */
void teclado(unsigned char key, int mx, int my) {
	switch (key) {
		case 27:
			exit(0);
		break;
	}

	glutPostRedisplay();
}

/* Funcion auxiliar para pintar el triangulo aleatorio de 
 * Sierpinsky, selecciona al azar uno de los tres puntos que 
 * le mandes */
Punto seleccionaAzar(Punto p0, Punto p1, Punto p2) {
	int suerte = rand();

	if (suerte % 3 == 2) {
		return p2;
	} else if (suerte % 3 == 1) {
		return p1;
	} else {
		return p0;
	}
}

/* Funcion auxiliar para pintar el triangulo aleatorio de Sierpinsky
 * selecciona al azar uno de cuatro colores disponibles */ 
void randomColor(float color[]) {
	int suerte = rand();

	if (suerte % 4 == 3) { /* Rojo */
		color[0] = 1.0;
		color[1] = 0.0;
		color[2] = 0.0;
	} else if (suerte % 4 == 2) { /* Verde */
		color[0] = 0.0;
		color[1] = 1.0;
		color[2] = 0.0;
	} else if (suerte % 4 == 1) { /* Azul */
		color[0] = 0.0;
		color[1] = 0.0;
		color[2] = 1.0;
	} else { /* Amarillo */
		color[0] = 1.0;
		color[1] = 1.0;
		color[2] = 0.0;
	}

	return;
}
